package com.zhiyou.app.shop.manager.dao.impl;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.zhiyou.app.shop.common.util.Page;
import com.zhiyou.app.shop.manager.dao.IBaseDAO;
import com.zhiyou.app.shop.manager.dao.pojo.BasePojo;
import com.zhiyou.app.shop.manager.dao.pojo.Goods;


/**
 * Data access object (DAO) for domain model
 * @author kewei
 */
@Repository
public class BaseHibernateDAO<T , PK extends Serializable> implements IBaseDAO<T , PK>{
	
	private static Log log = LogFactory.getLog(BaseHibernateDAO.class);
	@Autowired
	private SessionFactory sessionFactory;
	
	private Class<T> type;

	public BaseHibernateDAO(){}
	
	/**
	 * @param type
	 */
	public BaseHibernateDAO(Class<T> type) {
		super();
		this.type = type;
	}
	
	public Session getSession() {
		
		return sessionFactory.getCurrentSession();
	}
	
	
	/**
	 * 保存po对象
	 * @param transientInstance
	 * @throws DAOException
	 */
	public void save(T transientInstance){
		log.debug("save "+transientInstance.getClass().getName()+" instance");
		getSession().save(transientInstance);
		log.debug("save successful");
	}
	/**
	 * 删除po对象
	 * @param persistentInstance
	 * @throws DAOException
	 */
	public void delete(T persistentInstance){
		log.debug("delet "+persistentInstance.getClass().getName()+"  instance");
		getSession().delete(persistentInstance);
	}
	
	/**
	 * 根据实体id进行逻辑删除,id形式为1,2,3
	 */
	public void logicBatchDeleteByIds(String ids) {
		String hql = "update "+type.getName()+" set isDelete=true where id in ("+ids+")";
		Query query = getSession().createQuery(hql);
		query.executeUpdate();
	}

	/**
	 * 根据实体id进行物理删除,id形式为1,2,3
	 */
	public void deleteByIds(String ids) {
		String hql = "delete from "+type.getName()+" where id in ("+ids+")";
		Query query = getSession().createQuery(hql);
		query.executeUpdate();
	}
	
	/**
	 * 修改po对象
	 * @param transientInstance
	 * @throws DAOException
	 */
	public void update(T transientInstance){
		log.debug("updat "+transientInstance.getClass().getName()+" instance");
		getSession().update(transientInstance);
		log.debug("update successful");
	}
	
	public T findById(PK pk) {
		log.debug("find instance by id: " + pk);
		return (T) getSession().get(type, pk);
	}
	
	
	public List<T> findAll(){
		return this.findAll(null);
	}
	
	
	public List<T> findAll(Order ... order) {
		Criteria cri = getSession().createCriteria(type.getName());
		addOrderToCriteria(cri, order);
		return cri.list();
	}
	
	/**
	 * 只支持对象简单属性的查询，对对象中包含的其它复杂对象无法解析
	 */
	public List<T> findByExample(T t) {
		
		return this.findByExample(t, null);
		
	}
	
	public List<T> findByExample(T t , Order ... order) {
		
		log.debug("find instance by example");
		
		Criteria cri = getSession().createCriteria(t.getClass().getName());
		
		cri = cri.add(Example.create(t));
		
		addOrderToCriteria(cri, order);
		
		List results = cri.list();
		
		log.debug("find by example successful, result size: "+ results.size());
		
		return results;
	}

	public void findByExample(Page page , T t , Order ... order) {
		
		log.debug("find instance by example");
		
		Criteria cri = getSession().createCriteria(t.getClass().getName());
		
		cri = cri.add(Example.create(t));
		
		setPageProperties(page , cri);
		
		addOrderToCriteria(cri, order);
		
		List<T> results = cri.list();
		
		page.setResult(results);
		
		log.debug("find by example successful, result size: "+ results.size());
		
	}

	public List<T> findByCriteria(DetachedCriteria dCri , Order ... order) {
		log.debug("finding instance by DetachedCriteria");
		
		Criteria cri = dCri.getExecutableCriteria(getSession());
		
		addOrderToCriteria(cri, order);
		
		return cri.list();
	}
	
	
	public void findByCriteria(Page page , DetachedCriteria dCri , Order ... order) {
		log.debug("finding instance by DetachedCriteria");
		
		setPageTotalCountOnly(page , dCri);
		
		Criteria cri = dCri.getExecutableCriteria(getSession());
		
		addOrderToCriteria(cri, order);
		
		cri.setFirstResult(page.getOffset()).setMaxResults(page.getPageSize());
		
		List results = cri.list();
		
		page.setResult(results);
		
	}
	
	public void findByCriteria(Page page , DetachedCriteria dCri , List<Order> order) {
		log.debug("finding instance by DetachedCriteria");
		
		setPageTotalCountOnly(page , dCri);
		
		Criteria cri = dCri.getExecutableCriteria(getSession());
		
		if(null != order){
			
			for(Order o : order){
				cri.addOrder(o);
			}
		}
		
		cri.setFirstResult(page.getOffset()).setMaxResults(page.getPageSize());
		
		List results = cri.list();
		
		page.setResult(results);
		
	}
	
	public List<T> findByProperty(String propertyName, Object value){
		
		return this.findByProperty(propertyName, value, null);
	}
	
	
	
	@SuppressWarnings("unchecked")
	public List<T> findByProperty(String propertyName, Object value , Order ... order) {
		
		log.debug("finding instance with property: " + propertyName + ", value: " + value);
		
		Criteria cri = getSession().createCriteria(type.getName());
		
		cri.add(Restrictions.eq(propertyName, value));
		
		this.addOrderToCriteria(cri, order);
		
		return cri.list();
	}
	
	
	
	/**
	 * @param orderMap
	 * @return
	 */
	private void addOrderToCriteria(Criteria cri , Order ... orders) {
		if(null != orders){
			for(Order o : orders){
				cri.addOrder(o);
			}
		}
	}
	
	/**
	 * @param page
	 * @param cri
	 */
	private void setPageProperties(Page page, Criteria cri){
		
		if(page == null) return;
		
		
		/*获取总记录数开始*/
		Object totalCountObject = cri.setProjection(Projections.rowCount()).uniqueResult();
		
		int totalCount  = totalCountObject ==null ? 0 :Integer.valueOf(totalCountObject.toString());
		
		page.setTotalCount(totalCount);
		/*获取总记录数结束*/	
		
		cri.setProjection(null);
		
		cri.setFirstResult(page.getOffset()).setMaxResults(page.getPageSize());
	}
	
	private void setPageTotalCountOnly(Page page, DetachedCriteria dCri){
		Criteria cri = dCri.getExecutableCriteria(getSession());
		
		this.setPageProperties(page, cri);
	}
	
	protected String getOrderSql(Map<String, String> orderMap) {
		String orderSql="";
		if(orderMap!=null && orderMap.size()>0){
			Iterator<String> it = orderMap.keySet().iterator();
			orderSql=" order by ";
			int i=0;
			while(it.hasNext()){
				i++;
				String key = it.next();
				orderSql+=key+" "+orderMap.get(key);
				if(i>1 && i<orderMap.size()){
					orderSql+=" , ";
				}
			}
		}
		return orderSql;
	}
	/**基类调用此方法获取日志接口**/
	protected Log getLog(){
		return LogFactory.getLog(this.getClass());
	}
	
	public static void main(String[] args) {
		Order order = Order.desc("postTime");
		System.out.println(order.toString());
	}
}